from enum import Enum, unique, auto
from typing import Dict
import D5
from abc import ABC, abstractmethod
import threading
import functools
import shiboken2
from D5Core.D5Common import D5Singleton
import json

@unique
class APIRequestType(Enum):
    """API 请求类型常量"""
    SCENES_INFO = -9  # 获取场景信息的请求类型
    DELETE_MODEL = -8  # 删除模型的请求类型
    RENDER_PHOTO = -7  # 渲染照片的请求类型
    SWITCH_SCENE = -6  # 切换场景的请求类型
    UPDATE_CAMERA_INFO = -5  # 更新相机信息的请求类型
    CAMERA_INFO = -4  # 获取相机信息的请求类型
    CREATE_IMPORT_MODEL = -3  # 创建导入模型的请求类型
    SCREEN_SHOT = -2  # 截图的请求类型
    IMPORT_MODEL = -1  # 导入模型的请求类型

    
    READ_ORDINARY_ATTRIBUTE = 2  # 读取普通属性的请求类型
    WRITE_ORDINARY_ATTRIBUTE = 3  # 写入普通属性的请求类型
    READ_SPECIAL_ATTRIBUTE = 4  # 读取特殊属性的请求类型
    WRITE_SPECIAL_ATTRIBUTE = 5  # 写入特殊属性的请求类型

    GROUNP_COMMIT = 7  # 组提交的请求类型

    READ_MODEL_LIST = 8  # 获取模型列表的请求类型

    CREATE_LIGHT = 10  # 创建灯光的请求类型

    COPY_MODEL = 11  # 复制模型的请求类型


    




class APIEvent(Enum):
    START = "start"
    PROCESSING = "" 
    END = "end"
    NOW = "now"

# ==== 回调删除策略 ====
class DeleteStrategy(ABC):
    """回调删除策略基类"""
    @abstractmethod
    def should_delete(self, json_str):
        """判断是否应该删除回调"""
        pass

class ImmediateDeleteStrategy(DeleteStrategy):
    """立即删除策略"""
    def should_delete(self, json_str):
        return True

class NeverDeleteStrategy(DeleteStrategy):
    """永不删除策略"""
    def should_delete(self, json_str):
        return False


class CallbackData:
    """回调数据容器"""
    def __init__(self, message_id, callback, delete_strategy=None):
        self.message_id = message_id
        self.callback = callback
        # 默认使用立即删除策略
        self.delete_strategy = delete_strategy or ImmediateDeleteStrategy()

# 同步请求的响应容器
class SyncResponse:
    def __init__(self):
        self.event = threading.Event()
        self.data = None
        self.error = None
    
    def set_response(self, data):
        self.data = data
        self.event.set()
    
    def set_error(self, error):
        self.error = error
        self.event.set()
    
    def wait(self, timeout=None):
        return self.event.wait(timeout)

@D5Singleton
class D5Api:


    def __init__(self):
        from D5Core.D5Tools import ModelList, SceneTool
        self.__callback_map = {
            "select_model": CallbackData("select_model", lambda jsonstr:ModelList().emit_selected_model(jsonstr), NeverDeleteStrategy()),
            "scenes": CallbackData("scenes", lambda jsonstr:SceneTool().emit_scenes(jsonstr), NeverDeleteStrategy()),
        } # message_id: CallbackData

        self.__callback_map_lock = threading.RLock()  # 添加线程锁
        
        self.__enable_handle_map = {}  
        D5.setPluginResponseHandle(self.__response)
        D5.setPluginQueryMessageHandle(self.__queryMessage)
        D5.setPluginEnableHandle(self.__enable)

    def __queryMessage(self,messageID:str):
        return messageID in self.__callback_map
        
    def __enable(self, plugin_id,enable):
        if plugin_id in self.__enable_handle_map:
            self.__enable_handle_map[plugin_id](enable)
            
    def registeredEnableHandle(self, plugin_id ,enable_handle):
        self.__enable_handle_map[plugin_id] = enable_handle
    
    def registeredQwidget(self, plugin_id , qwidget):
        cpp_pointer = shiboken2.getCppPointer(qwidget)[0] 
        D5.sendQWidgetHandle(plugin_id, cpp_pointer)
    
    def request(self, message_id, json_str, callback, delete_strategy=None):
        """
        发送API请求
        
        Parameters:
        -----------
        plugin_id : str
            插件ID
        message_id : str
            消息ID
        json_str : str
            请求JSON字符串
        callback : function
            回调函数
        delete_strategy : DeleteStrategy, optional
            回调删除策略，默认为立即删除
        """
        with self.__callback_map_lock:  # 使用线程锁保护
            self.__callback_map[message_id] = CallbackData(
                message_id, 
                callback,
                delete_strategy
            )
        D5.sendD5ApiHandle(message_id,json_str)
    
    def request_sync(self, message_id, json_str, timeout=30):
        """
        同步发送API请求，等待响应
        
        Parameters:
        -----------
        plugin_id : str
            插件ID
        message_id : str
            消息ID
        json_str : str
            请求JSON字符串
        timeout : float, optional
            超时时间(秒)，默认30秒
            
        Returns:
        --------
        str : 响应的JSON字符串
        
        Raises:
        -------
        TimeoutError: 如果请求超时
        Exception: 回调处理中的任何异常
        """
        response = SyncResponse()
        
        def sync_callback(result_json):
            response.set_response(result_json)
        
        # 使用立即删除策略
        self.request(message_id, json_str, sync_callback, ImmediateDeleteStrategy())
        
        # 阻塞等待响应
        if not response.wait(timeout):
            # 超时后，清理回调
            with self.__callback_map_lock:  # 使用线程锁保护
                if message_id in self.__callback_map:
                    self.__callback_map.pop(message_id)
            raise TimeoutError(f"request timeout: {message_id}")
        
        # 检查是否有错误
        if response.error:
            raise response.error
            
        return response.data
    
    def get_model_data(self,unique_actor_id: str) -> Dict:
        """从API获取模型数据"""
        req_id = self.generate_uuid()
        
        payload = {
            "uuid": req_id,
            "type": APIRequestType.READ_ORDINARY_ATTRIBUTE.value,
            "content": """
            {
                "ActorId": "%s",
                "Param": ""
            }
            """%(unique_actor_id)
        }
        
        res = self.request_sync(req_id, json.dumps(payload))
        return json.loads(json.loads(res).get("content", "{}")).get(unique_actor_id, {})
    
    def __response(self, message_id, json_str):
        callback = None
        delete_callback = False
        # 使用线程锁保护对共享资源的访问
        with self.__callback_map_lock:
            if message_id in self.__callback_map:
                data = self.__callback_map[message_id]
                callback = data.callback
                
                # 在锁内判断是否需要删除回调，但实际删除在执行回调后
                if data.delete_strategy.should_delete(json_str):
                    delete_callback = True
        

        # 在锁外执行回调，避免长时间持有锁
        if callback:
            try:
                callback(json_str)
            except Exception as e:
                print(f"callback error: {str(e)}")
            
            # 回调执行完毕后，再次获取锁进行删除操作
            if delete_callback:
                with self.__callback_map_lock:
                    if message_id in self.__callback_map:
                        self.__callback_map.pop(message_id)
                        
    def generate_uuid(self):
        uuid:str = D5.generateUUID()
        return uuid
       
    
    def import_model_sync(self, model_path: str,timeout=30):
        """
        同步导入模型
        
        Parameters:
        -----------
        model_path : str
            模型文件路径
        timeout : float, optional
            超时时间(秒)，默认30秒
            
        Returns:
        --------
        str : 导入结果的JSON字符串
        
        Raises:
        -------
        TimeoutError: 如果导入超时
        Exception: 回调处理中的任何异常
        """
        response = SyncResponse()
        
        def sync_callback(result_json):
            response.set_response(result_json)
        
        class DeleteOnCompletionStrategy(DeleteStrategy):
            """完成后删除策略"""
            def should_delete(self, json_str):
                data = json.loads(json_str)
                return data.get("status") == 1

        message_id = self.generate_uuid()
        self.request(message_id, json.dumps({
            "uuid": message_id,
            "type": APIRequestType.IMPORT_MODEL.value,
            "content": json.dumps({
                "resource_path": model_path
            })}),
            sync_callback,
            DeleteOnCompletionStrategy()
       )
        
        while response.wait(timeout):
            if response.error:
                raise response.error
        
            if response.data:
                data = json.loads(response.data)
                if "status" in data and data["status"] == 1:
                    break
                else:
                    response.event.clear()  
        
        if response.error:
            raise response.error
            
        return response.data



    
    def render_photo_sync(self, message_id: str, json_str: str, timeout=100):
        response = SyncResponse()
        
        def sync_callback(result_json):
            response.set_response(result_json)
        class Delete(DeleteStrategy):
            """完成后删除策略"""
            def should_delete(self, json_str):
                data = json.loads(json_str)
                return data.get("percent") == 100

        self.request(message_id, json_str, sync_callback,Delete())

        while response.wait(timeout):
            if response.error:
                raise response.error
            if response.data:
                data = json.loads(json_str)
                if "percent" in data and data["percent"] == 100:
                    break
                else:
                    response.event.clear()  
        
        if response.error:
            raise response.error
            
        return response.data

    

